import { subMinutes, format, addMinutes } from 'date-fns'
import { ElMessage } from 'element-plus'
import router from '@/router'
import { getLocalStorage } from '../storage'
import useStorage from '@/hooks/cloud/storage'
const { getStorage } = useStorage()
/**
 * 精准判断对象类型
 * @param obj
 */
export function typeOf(obj) {
  const toString = Object.prototype.toString
  const map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object'
  }
  return map[toString.call(obj)]
}

/**
 * 深拷贝
 * @param data
 */
export function deepCopy(data, isNewKey) {
  const t = typeOf(data)
  let o

  if (t === 'array') {
    o = []
  } else if (t === 'object') {
    o = {}
  } else {
    return data
  }

  if (t === 'array') {
    for (let i = 0; i < data.length; i++) {
      o.push(deepCopy(data[i]))
    }
  } else if (t === 'object') {
    for (const i in data) {
      if (i === 'key' && isNewKey) {
        o[i] = uuid()
      } else {
        o[i] = deepCopy(data[i])
      }
    }
  }
  return o
}

/**
 * 深覆盖
 * @param target
 * @param merged
 */
export function deepMerge(target, merged) {
  for (const key in merged) {
    if (target[key] && typeof target[key] === 'object') {
      deepMerge(target[key], merged[key])
      continue
    }
    if (typeof merged[key] === 'object') {
      target[key] = deepCopy(merged[key])

      continue
    }
    target[key] = merged[key]
  }

  return target
}

/**
 * 深度对象合并
 * @param target
 * @param merged
 */
export function deepMergeObject(target, other) {
  const targetToString = Object.prototype.toString.call(target)
  const otherToString = Object.prototype.toString.call(target)
  if (targetToString === '[object Object]' && otherToString === '[object Object]') {
    for (let [key, val] of Object.entries(other)) {
      if (!target[key]) {
        target[key] = val
      } else {
        target[key] = deepMergeObject(target[key], val)
      }
    }
  } else if (targetToString === '[object Array]' && otherToString === '[object Array]') {
    for (let [key, val] of Object.entries(other)) {
      if (target[key]) {
        target[key] = deepMergeObject(target[key], val)
      } else {
        target.push(val)
      }
    }
  } else {
    return other || target
  }
  return target
}

/**
 * 节流函数，(限制函数的执行频率)返回函数连续调用时，空闲时间必须大于或等于 wait，func 才会执行
 * @param  {function} func        回调函数
 * @param  {number}   wait        表示时间窗口的间隔
 * @param immediate 是否立即执行 true 则先调用，false不先调用
 * @return {function}             返回客户调用函数
 */
export function throttle(func, wait, immediate) {
  let timeoutID
  let lastExec = 0

  function wrapper() {
    const self = this
    const elapsed = Number(new Date()) - lastExec
    const args = arguments

    function clearExistingTimeout() {
      if (timeoutID) {
        clearTimeout(timeoutID)
      }
    }

    function clear() {
      timeoutID = undefined
    }

    function exec() {
      lastExec = Number(new Date())
      func.apply(self, args)
    }

    if (immediate && !timeoutID) {
      exec()
    }
    clearExistingTimeout()
    if (immediate === undefined && elapsed > wait) {
      exec()
    } else {
      timeoutID = setTimeout(immediate ? clear : exec, immediate === undefined ? wait - elapsed : wait)
    }
  }

  return wrapper
}

/**
 * 防抖函数，(限制函数的执行频率) 保证再一系列调用时间内，只调用一次
 *
 * @param  {function} func        回调函数
 * @param  {number}   wait        表示时间窗口的间隔
 * @param  {boolean}   immediate   初始是否立即执行
 * @return {function}             返回客户调用函数
 */
export function debounce(func, wait, immediate) {
  let timer = null
  let isUsed = false
  return function () {
    const args = arguments

    // 是否立即执行
    if (immediate && !isUsed) {
      isUsed = true
      func.apply(this, args)
    } else {
      if (timer) clearTimeout(timer)
      timer = setTimeout(() => {
        func.apply(this, args)
        timer = null
      }, wait)
    }
  }
}

/**
 * 判断是否是对象或数组
 * @param obj
 * @returns {boolean}
 */
export function isObject(obj) {
  return typeof obj === 'object' && obj !== null
}

/**
 * 判定对象数组相等
 * @param obj1
 * @param obj2
 * @returns {boolean}
 */
export function isEqual(obj1, obj2) {
  // 两个数据有任何一个不是对象或数组
  if (!isObject(obj1) || !isObject(obj2)) {
    // 值类型(注意：参与equal的一般不会是函数)
    return obj1 === obj2
  }
  // 如果传的两个参数都是同一个对象或数组
  if (obj1 === obj2) {
    return true
  }

  // 两个都是对象或数组，而且不相等
  // 1.先比较obj1和obj2的key的个数，是否一样
  const obj1Keys = Object.keys(obj1)
  const obj2Keys = Object.keys(obj2)
  if (obj1Keys.length !== obj2Keys.length) {
    return false
  }

  // 如果key的个数相等,就是第二步
  // 2.以obj1为基准，和obj2依次递归比较
  for (let key in obj1) {
    // 比较当前key的value  --- 递归
    const res = isEqual(obj1[key], obj2[key])
    if (!res) {
      return false
    }
  }

  // 3.全相等
  return true
}

/**
 * 过滤数组数字
 * @param array
 * @returns {*}
 */
export function filterNonNumber(array) {
  return array.filter(n => typeof n === 'number')
}

/**
 * 数组数字求和
 * @param nums
 * @returns {*}
 */
export function mulAdd(nums) {
  const newNums = filterNonNumber(nums)
  return newNums.reduce((all, num) => all + num, 0)
}

/**
 * 合并相同的stack data
 * @param item
 * @param series
 * @returns {*[]}
 */
export function mergeSameStackData(item, series) {
  const stack = item.stack

  if (!stack) return [...item.data]

  const stacks = series.filter(({ stack: s }) => s === stack)

  const index = stacks.findIndex(({ data: d }) => d === item.data)

  const datas = stacks.splice(0, index + 1).map(({ data }) => data)

  const dataLength = datas[0].length

  return new Array(dataLength).fill(0).map((foo, i) => mulAdd(datas.map(d => d[i])))
}

//过滤空对象
export const filterObject = object => {
  let newObject = {}
  Object.keys(object).forEach(item => {
    if (object[item] !== '') {
      newObject[item] = object[item]
    }
  })
  return newObject
}
//计算几分钟之前的时间
export const BeforeTime = val => {
  let nowDate = new Date()
  let lastDate = subMinutes(nowDate, val)
  return format(lastDate, 'yyyy-MM-dd HH:mm:ss')
}
//计算几分钟之后的时间
export const afterTime = val => {
  let nowDate = new Date()
  let afterDate = addMinutes(nowDate, val)
  return format(afterDate, 'yyyy-MM-dd HH:mm:ss')
}

//字符串对象转Date
const stringToDate = strDate => {
  let tempStrs = strDate.split(' ')

  let dateStrs = tempStrs[0].split('-')
  let year = parseInt(dateStrs[0], 10)
  let month = parseInt(dateStrs[1], 10) - 1
  let day = parseInt(dateStrs[2], 10)

  let timeStrs = tempStrs[1].split(':')
  let hour = parseInt(timeStrs[0], 10)
  let minute = parseInt(timeStrs[1], 10)
  let second = parseInt(timeStrs[2], 10)

  let date = new Date(year, month, day, hour, minute, second)
  return date
}

//计算两个时间的间隔（天、时、分、秒）
export const AfterTime = (startdate, Unit, value) => {
  //计算之后的时间AfterTime('2022-06-27 11:11:11', 'S', 100)
  let date = stringToDate(startdate)
  switch (Unit) {
    case 'D':
      date.setDate(date.getDate() + value)
      break
    case 'H':
      date.setHours(date.getHours() + value)
      break
    case 'F':
      date.setMinutes(date.getMinutes() + value)
      break
    case 'S':
      date.setSeconds(date.getSeconds() + value)
      break
  }
  let Y = date.getFullYear() //获取年
  let M = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 //获取月
  let D = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() //获取日
  let H = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() //获取小时
  let F = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() //获取分钟
  let S = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() //获取秒
  return `${Y}-${M}-${D} ${H}:${F}:${S}`
}

export const dataFormatt = date => {
  //获取日期
  let nowDate = date || new Date()
  let year = nowDate.getFullYear()
  let month = nowDate.getMonth() + 1 < 10 ? '0' + (nowDate.getMonth() + 1) : nowDate.getMonth() + 1
  let day = nowDate.getDate() < 10 ? '0' + nowDate.getDate() : nowDate.getDate()
  return year + '-' + month + '-' + day
}

/**
 * 判断是否JSON字符串
 * @param {string} str
 * @returns {boolean}
 */
export const isJSON = str => {
  if (typeof str == 'string') {
    try {
      let obj = JSON.parse(str)
      if (typeof obj == 'object' && obj) {
        return true
      } else {
        return false
      }
    } catch (err) {
      return false
    }
  }
  return false
}

//获取UUID

export const uuid = hasHyphen => {
  const str = hasHyphen ? 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' : 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'
  return str.replace(/[xy]/g, c => {
    const r = (Math.random() * 16) | 0
    const v = c === 'x' ? r : (r & 0x3) | 0x8
    return v.toString(16)
  })
}

export const timeSlot = step => {
  let date = new Date()
  date.setHours('00') // 时分秒设置从零点开始
  date.setSeconds('00')
  date.setUTCMinutes('00')
  let slotNum = [...Array((24 * 60) / step).keys()] // 算出多少个间隔
  let timeArr = slotNum.map(item => {
    let time = formatDate(Number(date.getTime()) + Number(step * 60 * 1000 * item))
    let hour = '',
      sec = ''
    time.getHours() < 10 ? (hour = '0' + time.getHours()) : (hour = time.getHours()) // 获取小时
    time.getMinutes() < 10 ? (sec = '0' + time.getMinutes()) : (sec = time.getMinutes()) // 获取分钟
    return hour + ':' + sec
  })
  timeArr = timeArr.concat(['24:00'])
  return timeArr
}

// 单位换算
export const unitTrans = (data, baseUnit, currentUnit) => {
  const hash = {
    Wh: ['Wh', 'kWh', 'MWh', 'GWh'],
    W: ['W', 'kW', 'MW', 'GW']
  }
  let positive = true
  if (data < 0) {
    positive = false
    data = -data
  }
  let count = data
  const unitArr = hash[baseUnit]
  let index = unitArr.indexOf(currentUnit)
  while (index < unitArr.length && count / 1000 > 1) {
    if (count / 1000 > 1) {
      count = count / 1000
      index++
    }
  }
  count = Math.round(count * 100) / 100
  return {
    data: positive ? count : -count,
    unit: unitArr[index]
  }
}

// 获取domainid
export const domainid = (isReal = false) => {
  let userInfo = getStorage('userInfo')
  let domainid = userInfo ? userInfo.domain_id : null

  if (isReal && domainid) {
    return domainid
  }
  const app_config = getLocalStorage('app_config') || {}

  if (router.currentRoute.value.name == 'ChartPreview' || router.currentRoute.value.name == 'ChartHome') {
    domainid = getStorage('previewDomainId') || domainid || app_config.domainId
  }
  return domainid
}
//解决 iPhone new Date时间问题
export const formatDate = date => {
  if (!date) return date
  try {
    if (typeof date == 'number') {
      return new Date(date)
    } else {
      return new Date(date.replace(/-/g, '/'))
    }
  } catch (error) {
    console.error(error)
  }
}

// 复制文本到粘贴板
export const copyText = (data, type = 'input') => {
  const input = document.createElement(type)
  input.value = data
  input.style = 'position:absolute;top:-500px;left:-500px'
  document.body.appendChild(input)
  input.select()
  document.execCommand('copy')
  document.body.removeChild(input)
  ElMessage.success('复制成功')
}

//导出json
export const createJSON = (data, filename) => {
  //参数1：json数据；参数2：自定义导出的文件名
  if (!data) {
    ElMessage({
      message: '无数据',
      type: 'error'
    })
    return
  }
  if (!filename) filename = '未定义.json' //如果没有文件名，设置默认文件名
  if (typeof data === 'object') data = JSON.stringify(data)
  let blob = new Blob([data], { type: 'text/json' })
  let e = document.createEvent('MouseEvents')
  let a = document.createElement('a')
  a.download = filename + '.json'
  a.href = window.URL.createObjectURL(blob)
  a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
  e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
  a.dispatchEvent(e)
}

//格式化json
export const formatJsonForNotes = (json, options) => {
  let reg = null,
    formatted = '',
    pad = 0,
    PADDING = '  ' // （缩进）可以使用'\t'或不同数量的空格
  // 可选设置
  options = options || {}
  // 在 '{' or '[' follows ':'位置移除新行
  options.newlineAfterColonIfBeforeBraceOrBracket =
    options.newlineAfterColonIfBeforeBraceOrBracket === true ? true : false
  // 在冒号后面加空格
  options.spaceAfterColon = options.spaceAfterColon === false ? false : true
  // 开始格式化...
  if (typeof json !== 'string') {
    // 确保为JSON字符串
    json = JSON.stringify(json)
  } else {
    //已经是一个字符串，所以解析和重新字符串化以删除额外的空白
    json = JSON.parse(json)
    json = JSON.stringify(json)
  }
  // 在花括号前后添加换行
  reg = /([\{\}])/g
  json = json.replace(reg, '\r\n$1\r\n')
  // 在方括号前后添加新行
  reg = /([\[\]])/g
  json = json.replace(reg, '\r\n$1\r\n')
  // 在逗号后添加新行
  reg = /(\,)/g
  json = json.replace(reg, '$1\r\n')
  // 删除多个换行
  reg = /(\r\n\r\n)/g
  json = json.replace(reg, '\r\n')
  // 删除逗号前的换行
  reg = /\r\n\,/g
  json = json.replace(reg, ',')
  // 可选格式...
  if (!options.newlineAfterColonIfBeforeBraceOrBracket) {
    reg = /\:\r\n\{/g
    json = json.replace(reg, ':{')
    reg = /\:\r\n\[/g
    json = json.replace(reg, ':[')
  }
  if (options.spaceAfterColon) {
    reg = /\:/g
    json = json.replace(reg, ': ')
  }
  json.split('\r\n').forEach(node => {
    let i = 0,
      indent = 0,
      padding = ''
    if (node.match(/\{$/) || node.match(/\[$/)) {
      indent = 1
    } else if (node.match(/\}/) || node.match(/\]/)) {
      if (pad !== 0) {
        pad -= 1
      }
    } else {
      indent = 0
    }
    for (i = 0; i < pad; i++) {
      padding += PADDING
    }
    formatted += padding + node + '\r\n'
    pad += indent
  })
  return formatted
}

export const getNowDate = formatStr => {
  let date = new Date()
  if (formatStr) {
    return format(date, formatStr)
  }
  return date
}

// 时间戳 -> 格式化日期
export function timeStampToDate(timeStamp, type = 'date') {
  if (!timeStamp) return ''
  timeStamp = timeStamp.toString().length === 13 ? timeStamp : timeStamp * 1000
  const date = new Date(timeStamp)
  const Y = date.getFullYear().toString()
  const M = (date.getMonth() + 1).toString().padStart(2, '0')
  const D = date.getDate().toString().padStart(2, '0')
  let h = ''
  let m = ''
  let s = ''
  if (type === 'datetime') {
    h = date.getHours().toString().padStart(2, '0')
    m = date.getMinutes().toString().padStart(2, '0')
    s = date.getSeconds().toString().padStart(2, '0')
    return `${Y}-${M}-${D} ${h}:${m}:${s}`
  }
  return `${Y}-${M}-${D}`
}
